<template>
  <div class="haloe-image" ref="image" :style="imageStyles">
    <div v-if="loading" class="haloe-image-placeholder">
      <slot name="placeholder">
        <span>{{ loadingLang }}</span>
      </slot>
    </div>
    <div v-else-if="imageError" class="haloe-image-error">
      <slot name="error">
        <span>{{ failLang }}</span>
      </slot>
    </div>
    <div v-if="loadingImage" :class="innerClasses" @click="handlePreview">
      <img
        :class="imgClasses"
        :style="[fitStyle]"
        :alt="alt"
        :src="src"
        :loading="loadingType"
        :referrerPolicy="referrerPolicy"
        @load="handleImageLoad"
        @error="handleImageError" />
      <slot v-if="preview&&previewTip" name="preview">
        <div class="haloe-image-mark">
          <span>{{ previewLang }}</span>
        </div>
      </slot>
    </div>
    <template v-if="preview">
      <ImagePreview
        v-model="imagePreviewModal"
        :preview-list="previewList"
        :initial-index="initialIndex"
        :infinite="infinite"
        :mask-closable="maskClosable"
        :transfer="transfer"
        :toolbar="toolbar"
        @on-close="handleClose"
        @on-switch="handleSwitch"
      />
    </template>
  </div>
</template>

<script>
import Locale from '../../mixins/locale';
export default {
  mixins:[Locale],
  computed:{
    loadingLang() {
      return this.t('i.select.loading')
    },
    failLang() {
      return this.t('i.image.fail')
    },
    previewLang() {
      return this.t('i.image.preview')
    },
  }
}
</script>

<script setup>
import { getCurrentInstance,ref, computed,  onMounted,onBeforeUnmount } from 'vue';
import { isClient } from '../../utils/index';
import ImagePreview  from './image-preview.vue';

const prefixCls='haloe-image'

const isElement=(el)=>{
  return typeof HTMLElement ==="object"&& el instanceof HTMLElement
}

const emits=defineEmits(['on-load', 'on-error', 'on-switch', 'on-close', 'on-click'])
const props=defineProps({
  src: {
    type: String,
    default: ''
  },
  alt: {
    type: String,
    default: ''
  },
  preview: {
    type: Boolean,
    default: false
  },
  referrerPolicy: {
    type: String,
    default: ''
  },
  width: {
    type: [String, Number],
    default: ''
  },
  height: {
    type: [String, Number],
    default: ''
  },
  // https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
  fit: {
    type: String, // 'fill' | 'contain' | 'cover' | 'none' | 'scale'-down'
    default: ''
  },
  lazy: {
    type: Boolean,
    default: false
  },
  scrollContainer: {
    type: [String],
    default: ''
  },
  transfer: {
    type: Boolean,
    default () {
      const global = getCurrentInstance().appContext.config.globalProperties;
      return !global.$HALOE || global.$HALOE.transfer === '' ? false : global.$HALOE.transfer;
    }
  },
  maskClosable: {
    type: Boolean,
    default: true
  },
  // preview list
  previewList: {
    type: Array
  },
  infinite: {
    type: Boolean,
    default: true
  },
  initialIndex: {
    type: Number,
    default: 0
  },
  previewTip: {
    type: Boolean,
    default: true
  },
  toolbar: {
    type: Array,
    default() {
      const global = getCurrentInstance().appContext.config.globalProperties;
      return !global.$HALOE || !global.$HALOE.image || global.$HALOE.image.toolbar === ''
        ? ['zoomIn', 'zoomOut', 'original', 'rotateLeft', 'rotateRight', 'download']
        : global.$HALOE.image.toolbar;
    }
  }
})

let loadingImage=ref(false)
let loading=ref(false)
let imageError=ref(false)
let scrollElement=ref()
let observer=ref()
let imagePreviewModal=ref(false)

let image=ref()

const innerClasses=computed(()=>{
  return [
    `${prefixCls}-inner`,
    {
      [`${prefixCls}-cursor`] :props.preview
    }
  ]
})

const imgClasses=computed(()=>{
  return [
    `${prefixCls}-img`,
    {
      [`${prefixCls}-img-hidden`]: loading.value || imageError.value
    }
  ]
})

const fitStyle=computed(()=>{
  const fitContains = ['fill', 'contain', 'cover', 'none', 'scale-down'];
  return fitContains.includes(props.fit) ? `object-fit:${props.fit};` : '';
})

const imageStyles=computed(()=>{
  return {
    width: typeof props.width === 'number' ? `${props.width}px` : props.width,
    height: typeof props.height === 'number' ? `${props.height}px` : props.height
  };
})

const loadingType=computed(()=>{
  return props.lazy ? 'lazy' : 'eager';
})


const offObserver=()=>{
  observer.value&&observer.value.disconnect()
}

const loadImage=()=>{
  loading.value=true;
  imageError.value=false;
  loadingImage.value=true;
}

const handlerObserveImage=(entries)=>{
  for(let entry of entries){
    if(entry.isIntersecting){
      offObserver()
      loadImage()
    }
  }
}

const handleLazy=()=>{
  const $el = image.value;
  const _observer = observer.value = new IntersectionObserver(handlerObserveImage, {
    root: scrollElement.value,
    rootMargin: "0px",
    threshold: 0
  });
  _observer.observe($el);
}

const addLazyImageListener=()=>{
  scrollElement.value=null;
  if(isElement(props.scrollContainer)){
    scrollElement.value=props.scrollContainer
  }else if(props.scrollContainer && typeof props.scrollContainer === 'string'){
    scrollElement.value=document.querySelector(props.scrollContainer)
  }
  handleLazy()
}

const handleImageLoad=()=>{
  loading.value=false;
  imageError.value=false;
  emits("on-load")
}

const handleImageError=()=>{
  loading.value=false
  imageError.value=true
  loadingImage.value=false
  emits("on-error")
}


const handleImageEvent=()=>{
  props.lazy?addLazyImageListener():loadImage()
}

const handlePreview=()=>{
  if(props.preview){
    imagePreviewModal.value=true;
    const initialIndex =props.initialIndex
    emits("on-click",{initialIndex})
  }
}

const handleClose=()=>{
  emits("on-close")
}

const handleSwitch=(params)=>{
  emits("on-switch",params)
}

onMounted(() => {
  isClient && handleImageEvent();
})
onBeforeUnmount(() => {
  offObserver()
})
</script>
<style>

</style>